/***************************************************************
 * 系统名称           : 生鲜MIS系统
 * 处理内容           : 继承GridControl，并初始化GridView，增加相关通用方法或事件
 * @author            : 
 * @Modify            : geping@2015-10-14
 * @description       : 
 * @version           : v1.0
 * @Copyright         : 宁波康铭泰克信息科技有限公司
 ***************************************************************/
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Threading;
//using System.Reflection;
using System.Windows.Forms;
using Comteck.Winforms.Controls.Components;
using Comteck.Winforms.Infrastructure;
using Comteck.Winforms.Utils;
using DevExpress.Data;
using DevExpress.Data.Filtering;
using DevExpress.Utils;
using DevExpress.Utils.Serializing;
using DevExpress.XtraEditors;
using DevExpress.XtraEditors.Repository;
using DevExpress.XtraGrid;
using DevExpress.XtraGrid.Columns;
using DevExpress.XtraGrid.Registrator;
using DevExpress.XtraGrid.Views.BandedGrid;
using DevExpress.XtraGrid.Views.Base;
using DevExpress.XtraGrid.Views.Base.Handler;
using DevExpress.XtraGrid.Views.Base.ViewInfo;
using DevExpress.XtraGrid.Views.Grid;
using DevExpress.XtraGrid.Views.Grid.Drawing;
using DevExpress.XtraGrid.Views.Grid.ViewInfo;

namespace Comteck.Winforms.Controls {
  #region MyGridControl

  /// <summary>
  /// A grid view is represented by several classes. 
  ///   Please make sure that you inherit the appropriate class to implement a specific functionality:
  /// GridView - a central grid view class, which manages columns, view infos, a painter, mouse and keyboard handlers.
  /// GridHandler - processes the mouse and keyboard.
  /// GridViewInfo - calculates view information for drawing a grid view on screen.
  /// GridViewPrintInfo - calculates the information necessary to print a grid view.
  /// 
  /// Assume, you have created a GridView descendant and want to use your class with the GridControl at design time. 
  ///   You should do the following to register it:
  /// 1. Create a Registrator class (please see GridInfoRegistrator in ViewRegistrator.cs). 
  ///   Please note: you will need to be careful when overriding the ViewName property, 
  ///     because it is used to identify the view within the GridControl's descendant. 
  ///   Please make certain that you are using the same view name both in the registrator's ViewName property 
  ///     and within the GridControl's CreateDefaultView overridden method.
  /// 2. Inherit the GridControl class and override the RegisterAvailableViewsCore 
  ///   method (it was the RegisterAvailableViews method in XtraGrid 2).
  /// 3. If you want the GridControl to create an instance of your GridView by default,
  /// you should override the GridControl.CreateDefaultView method.
  /// 
  /// 创建自定义的MyGridControl
  /// <para>https://www.devexpress.com/Support/Center/Search/List/1?searchString=treelist</para>
  /// <para>http://www.dxper.net/thread-555-1-1.html</para>
  /// </summary>
  [ToolboxItem(true)]
  public class MyGridControl : GridControl {
    /// <summary>
    /// Create a new MyGridControl object
    /// </summary>
    public MyGridControl()
        : base() {
      // 使用内置的分页
      this.CustomInit();
    }

    /// <summary>
    /// 创建初始化的GridView视图（请参照第3点）
    /// </summary>
    /// <returns></returns>
    protected override BaseView CreateDefaultView() {
      return base.CreateView("MyGridView");
    }

    /// <summary>
    /// 向容器中注册所有的控件
    /// </summary>
    /// <param name="collection"></param>
    protected override void RegisterAvailableViewsCore(InfoCollection collection) {
      base.RegisterAvailableViewsCore(collection);
      collection.Add(new MyGridViewInfoRegistrator());
      collection.Add(new MyBandedGridInfoRegistrator());
    }

    //
    // 摘要:
    //     Gets or sets whether the embedded data navigator is visible.
    [Browsable(true)]
    [DefaultValue(true)]
    [DXCategory("Appearance")]
    public override bool UseEmbeddedNavigator { get; set; } = true;

    /// <summary>
    /// 初始化内置的分页
    /// </summary>
    private void InitNavigator() {
      //if (this.UseEmbeddedNavigator == true) {
      this.EmbeddedNavigator.TextStringFormat = "记录 {0} / {1}";
      this.EmbeddedNavigator.Buttons.Append.Visible = false;
      this.EmbeddedNavigator.Buttons.Remove.Visible = false;
      this.EmbeddedNavigator.Buttons.Edit.Visible = false;
      this.EmbeddedNavigator.Buttons.EndEdit.Visible = false;
      this.EmbeddedNavigator.Buttons.CancelEdit.Visible = false;
      //}
    }

    private void CustomInit() {
      this.InitNavigator();

      // 针对gridcontrol,如果设置dock=fill，则findpanel就会BUG，最小化后再还原界面就无法显示，但是原先的查找框会消失
      // 针对这个BUG需要在主界面的最大最小化事件中修改，暂时没有去处理
      // modify by weijie.sun 2017/9/14
      this.ProcessGridKey += (sender, e) => {
        var view = (sender as GridControl).FocusedView as ColumnView;
        if (view == null) {
          return;
        }
        
        if (e.Control && e.KeyCode == Keys.F) {
          if (view.OptionsBehavior.Editable) {
            view.CloseEditor();
          }

          if (view.IsFindPanelVisible) {
            view.HideFindPanel();
          } else {
            view.ShowFindPanel();
          }

          e.Handled = true;
        }
      };
    }
                                    
    public void SetDataSource(object dataSource) {
      this.BeginUpdate();
      this.DataSource = dataSource;
      this.EndUpdate();
    }
  }

  #endregion

  #region 创建自定义的GridView
  /// <summary>
  /// 创建自定义的GridView
  /// </summary>
  public class MyGridView : GridView {
    /// <summary>
    /// 当光标在末行最后一个指定字段时继续回车的跳转方式
    /// </summary>
    public enum EnumForwardType {
      [Description("跳转到首行首列")]
      Circle = 0,
      [Description("停止跳转，直接返回")]
      Stop = 1,
      [Description("新增一行，光标定位到新行指定列")]
      NewRow = 2,
    }

    /// <summary>
    /// Default View Name
    /// </summary>
    protected override string ViewName => "MyGridView";

    public delegate void AddNewLineDelegate();
    /// <summary>
    /// 新增行自定义事件  add by geping@2017-01-09
    /// </summary>
    public AddNewLineDelegate AddNewLineEventHandler;

    public RowCellClickEventHandler RowCellClickEventHandler;
    public SelectionChangedEventHandler SelectionChangedEventHandler;
    public EventHandler ColumnFilterChangedEventHandler;

    #region 自定义属性
    //
    // 摘要:                                                        
    //     当 GridView 没有数据时是否显示提示，提示的值由Tag属性设置   geping@2015-10-14
    [DefaultValue(false),]
    [DXCategory("自定义属性")]
    [Description("当 GridView 没有数据时是否显示提示，提示的值由 EmptyForegroundText 属性设置")]
    [XtraSerializableProperty]
    public virtual bool EnableShowEmptyForeground { get; set; }
    //
    // 摘要:
    //     当 GridView 没有数据时的默认提示.   geping@2015-10-14
    [DefaultValue(typeof(string), "")]
    [DXCategory("自定义属性")]
    [Description("当 GridView 没有数据时显示的值，默认显示\"没有查询到你所想要的数据!\"")]
    [XtraSerializableProperty]
    public virtual string EmptyForegroundText { get; set; }
    //
    // 摘要:
    //     GridView 回车时按指定字段跳转到相应的单元格.              geping@2016-11-25
    //     调用方法如：new List<string>() { "SKUCODE", "PRICE", "DESCRIPTION" };
    [DefaultValue(typeof(List<string>), "")]
    [DXCategory("自定义属性")]
    [Description("指定可以跳转的列（FieldName)，回车时按值先后顺序跳转")]
    [XtraSerializableProperty]
    [TypeConverter(typeof(CollectionConverter))]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    [Browsable(false)]
    public virtual List<string> ForwardColumnList { get; set; }
    //
    // 摘要:
    //     当光标在末行最后一个指定字段时继续回车的跳转方式.     geping@2016-12-15
    [DefaultValue(typeof(EnumForwardType), "Circle")]
    [DXCategory("自定义属性")]
    [Description("当光标在末行最后一个指定字段时继续回车的跳转方式")]
    [XtraSerializableProperty]
    public virtual EnumForwardType ForwardType { get; set; } = EnumForwardType.Circle;
    //
    // 摘要:
    //     列匹配的模式.  
    [DefaultValue(EnumFilterColumnMode.Visible)]
    [DXCategory("自定义属性")]
    [Description("列匹配的模式")]
    [XtraSerializableProperty]
    public virtual EnumFilterColumnMode FilterColumnMode { get; set; } = EnumFilterColumnMode.Visible;
    //
    // 摘要:
    //     当末行最后一列回车新增行时，指定光标在新行定位的列（FieldName).     geping@2016-12-20
    [DefaultValue(typeof(string), "")]
    [DXCategory("自定义属性")]
    [Description("当末行最后一列回车新增行时，指定光标在新行定位的列（FieldName)，默认为新增行首个可编辑列")]
    [XtraSerializableProperty]
    public virtual string LocatedColumn { get; set; }
    //
    // 摘要:
    //     是否启用基类右键弹出 PopupMenuShowing 事件.     geping@2016-02-20
    [DefaultValue(true)]
    [DXCategory("自定义属性")]
    [Description("是否启用基类鼠标右键 PopupMenuShowing 事件")]
    [XtraSerializableProperty]
    public virtual bool EnablePopupMenuShowing { get; set; } = true;
    //
    // 摘要:
    //     当 MultiSelect = True,MultiSelectMode = CheckBoxRowSelect 时，指定某个字段与默认复选框同步状态.   geping@2021-07-02
    [DefaultValue(typeof(string), "")]
    [DXCategory("自定义属性")]
    [Description("当 MultiSelect = True，MultiSelectMode = CheckBoxRowSelect 时，指定某个字段与默认复选框同步状态")]
    [XtraSerializableProperty]
    public virtual string CheckBoxSelectorField { get; set; }
    // 有问题
    // 摘要:
    //     设置 GridView 中的所有列是否允许编辑，包含内置控件，如：repositoryItemCheckEdit.     geping@2021-05-08
    //[DefaultValue(true)]
    //[DXCategory("自定义属性")]
    //[Description("设置 GridView 中的所有列是否允许编辑，包含内置控件，如：repositoryItemCheckEdit. ")]
    //[XtraSerializableProperty]
    //public virtual bool AllowEditable {
    //  get { return _allowEditable; }
    //  set {
    //    // 忽略 GridLookupEdit 控件，否则设计器会报“未将对象引用设置到实例”的错误
    //    if (this.GridControl == null) { return; }

    //    _allowEditable = value;

    //    this.OptionsBehavior.Editable = _allowEditable;

    //    foreach (GridColumn col in this.Columns) {
    //      col.OptionsColumn.AllowEdit = _allowEditable;

    //      if (!_allowEditable && col.ColumnEdit != null) {
    //        col.OptionsColumn.AllowEdit = false;
    //      }
    //    }

    //    this.Invalidate();
    //  }
    //}
    //private bool _allowEditable = true;

    //
    // 摘要:
    //     将指定列绑定到 CheckBoxRowSelect 列.     geping@2020-07-31
    //[DefaultValue(true)]
    //[DXCategory("功能扩展")]
    //[Description("将指定列绑定到 CheckBoxRowSelect 列")]
    //public virtual string CheckBoxSelectorField {
    //  get { return _checkBoxSelectorField; }
    //  set {                              
    //    _checkBoxSelectorField = value;

    //    if (_checkBoxSelectorField.IsNullOrEmpty()) { return; }

    //    if (this.Columns.All(x => x.FieldName != _checkBoxSelectorField)) {
    //      throw new ArgumentException($@"列[{_checkBoxSelectorField}]不存在");
    //    }

    //    // 隐藏指定列
    //    //this.Columns[_checkBoxSelectorField].Visible = false;

    //    var t = this.OptionsSelection.GetType();
    //    var obj = Activator.CreateInstance(t);

    //    // 调用对象属性
    //    var property = t.GetProperty("CheckBoxSelectorField", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) ?? throw new ArgumentNullException($@"对象没有找到 CheckBoxSelectorField 属性");
    //    var method = t.GetMethod("set_CheckBoxSelectorField", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
    //    // 将指定值赋值给目标对象
    //    //property.SetValue(this.OptionsSelection, _checkBoxSelectorField);
    //    //method.Invoke(null, new object[] { _checkBoxSelectorField) });    
    //    //method.Invoke(obj, new object[] { _checkBoxSelectorField });
    //    property.SetValue(obj, _checkBoxSelectorField, null);
    //    var property1 = t.GetProperty("MultiSelect", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) ?? throw new ArgumentNullException($@"对象没有找到 CheckBoxSelectorField 属性");

    //    property1.SetValue(obj, true, null);

    //    var property2 = t.GetProperty("CheckBoxSelectorColumnWidth", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) ?? throw new ArgumentNullException($@"对象没有找到 CheckBoxSelectorField 属性");

    //    property2.SetValue(obj, 60, null);

    //    var e = t.GetEvent("OnOptionChanged", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
    //    //e.AddEventHandler(obj, OnOptionChanged);
    //  }
    //}
    //private string _checkBoxSelectorField;
    #endregion

    #region 构造函数

    /// <summary>
    /// 默认构造
    /// </summary>
    public MyGridView() {
      this.OptionsFilter.ColumnFilterPopupMode = DevExpress.XtraGrid.Columns.ColumnFilterPopupMode.Classic;
    }

    /// <summary>
    /// 含参构造，主要用于动态构建GridView
    /// </summary>
    /// <param name="grid"></param>
    public MyGridView(GridControl ownerGrid)
        : base(ownerGrid) {
      Initialize();
      
    }

    #endregion

    #region 自定义方法

    /// <summary>
    /// 初始化，设置默认值
    /// </summary>
    public void Initialize() {
      this.EnablePopupMenuShowing = true;
      // 设置为多选
      this.OptionsSelection.MultiSelect = true;
      // 自带勾选列的宽度
      this.OptionsSelection.CheckBoxSelectorColumnWidth = 35;
      // 选中序号列是否勾选
      this.OptionsSelection.UseIndicatorForSelection = false;
      // 隐藏提示
      this.OptionsView.ShowGroupPanel = false;
      // 是否自适应列宽度
      this.OptionsView.ColumnAutoWidth = false;
      // 是否启用偶数行外观                                               
      this.OptionsView.EnableAppearanceEvenRow = true;
      // 是否启用奇数行外观
      this.OptionsView.EnableAppearanceOddRow = true;
      // 是否显示过滤面板
      this.OptionsView.ShowFilterPanelMode = ShowFilterPanelMode.Default;
      // 是否显示子报表
      this.OptionsDetail.EnableMasterViewMode = false;
      //this.OptionsView.ShowFooter = true;

      //筛选模式
      this.OptionsFilter.ColumnFilterPopupMode = DevExpress.XtraGrid.Columns.ColumnFilterPopupMode.Classic;
      // 禁用单击列弹出右键菜单
      //this.OptionsMenu.EnableColumnMenu = true;

      // 禁止编辑
      //this.OptionsBehavior.Editable = false;
      // 编辑模式
      this.OptionsBehavior.EditorShowMode = EditorShowMode.MouseUp;

      // 打印效果
      this.OptionsPrint.AutoWidth = false;

      // 字段标题居中
      //this.Appearance.HeaderPanel.TextOptions.HAlignment = HorzAlignment.Center;

      // 选中行颜色
      this.Appearance.SelectedRow.BackColor = Color.FromArgb(180, 225, 255);
      this.Appearance.SelectedRow.ForeColor = Color.Black;
      this.Appearance.SelectedRow.Options.UseBackColor = true;
      this.Appearance.SelectedRow.Options.UseForeColor = true;
      // 焦点行颜色
      this.Appearance.FocusedRow.BackColor = Color.FromArgb(180, 225, 255);
      this.Appearance.FocusedRow.ForeColor = Color.Black;
      this.Appearance.FocusedRow.Options.UseBackColor = true;
      this.Appearance.FocusedRow.Options.UseForeColor = true;
      //this.SetAppearanceColor();

      // 是否回车后光标移到下一个单元格
      this.OptionsNavigation.EnterMoveNextColumn = true;

      // 是否允许移动列，默认允许移动列
      this.OptionsCustomization.AllowColumnMoving = true;

      // 默认选中第一行
      this.FocusedRowHandle = 0;
      // 设置行号宽度
      this.IndicatorWidth = 28;

      this.ColumnFilterChanged += this.MyGridView_ColumnFilterChanged;

      this.Focus();

      // 可编辑列设定   add by geping@2016-02-18
      //SetEditableColumns();

      // 以下两个事件放到 EndInit() 事件中 modify by geping
      //this.FocusedColumnChanged += this.MyGridView_FocusedColumnChanged;
      //this.ShownEditor += this.MyGridView_ShownEditor;
    }

    private void MyGridView_ColumnFilterChanged(object sender, EventArgs e) {
       this.CloseEditor();
    }

    /// <summary>
    /// 设置单元格是否可编辑
    /// <para>
    /// 1、增加可编辑字段的单元格背景色和标题字体色  add by geping@2016-05-23
    /// 2、列的内置控件的可编辑属性与该列保持一致  add by geping@2020-06-12
    /// </para>
    /// </summary>
    /// <param name="editableColumnsList">当GridView可编辑时，指定可以编辑的列(FieldName).</param>
    public void SetEditableColumns(List<string> editableColumnsList = null) {
      // 传入列表为空时，则单元格不允许编辑  add by geping@2020-06-10
      if (editableColumnsList.IsNullOrEmpty()) {
        foreach (GridColumn col in this.Columns) {
          // 设置标题字体色
          col.AppearanceHeader.ForeColor = Color.Black;
          col.OptionsColumn.AllowEdit = false;

          if (col.ColumnEdit != null) {
            col.OptionsColumn.AllowEdit = false;
          }
        }

        return;
      }

      if (this.OptionsBehavior.Editable && editableColumnsList.IsNotNullOrEmpty()) {
        foreach (GridColumn col in this.Columns) {
          if (editableColumnsList.Exists(x => x == col.FieldName)) {
            // 设置单元格背景色
            //col.AppearanceCell.BackColor = Color.FromArgb(255, 255, 192);
            // 设置标题字体色
            col.AppearanceHeader.ForeColor = Color.Blue;
            col.OptionsColumn.AllowEdit = true;

            if (col.ColumnEdit != null) {
              col.OptionsColumn.AllowEdit = true;
            }
          } else {
            // 设置单元格背景色
            //col.AppearanceCell.BackColor = Color.FromArgb(255, 255, 255);
            // 设置标题字体色
            col.AppearanceHeader.ForeColor = Color.Black;
            col.OptionsColumn.AllowEdit = false;

            if (col.ColumnEdit != null) {
              col.OptionsColumn.AllowEdit = false;
            }
          }
        }
      }
    }

    /// <summary>
    /// 设置GridView-隐藏列
    /// </summary>
    /// <param name="columnList">需要隐藏的FieldName</param>
    public void HideColumns(List<string> columnList) {
      if (columnList.IsNullOrEmpty()) return;

      foreach (GridColumn col in this.Columns) {
        if (columnList.Exists(x => x == col.FieldName)) {
          col.Visible = false;
        }
      }
    }

    /// <summary>
    /// 设置GridView-按指定的索引显示
    /// </summary>
    /// <param name="columnList">FieldName列名,VisibleIndex列索引</param>
    public void ShowColumns(Dictionary<string, int> columnList) {
      if (columnList.IsNullOrEmpty()) return;

      foreach (GridColumn col in this.Columns) {
        if (columnList.ContainsKey(col.FieldName)) {
          col.VisibleIndex = columnList[col.FieldName];
          col.Visible = true;
        }
      }
    }

    /// <summary>
    /// 根据DPI重新计算列宽
    /// </summary>
    private void ReCaculateWidthByDPI() {
      if (!this.OptionsView.ColumnAutoWidth) {
        var sizeF = SysGlobal.DpiSizeF == null ? this.GridControl.ScaleFactor : SysGlobal.DpiSizeF;
        foreach (GridColumn column in this.Columns) {
          column.Width = DevExpress.Skins.RectangleHelper.ScaleHorizontal(column.Width, sizeF.Width);
        }
      }
    }

    /// <summary>
    /// 判断是否为表格最后一列可编辑列 add by geping@2016-12-19
    /// </summary>
    /// <param name="focusedColumn"></param>
    /// <returns></returns>
    private bool IsLastEditableColumn(GridColumn focusedColumn) {
      var index = this.VisibleColumns.IndexOf(focusedColumn);

      for (int i = this.VisibleColumns.Count - 1; i >= 0; i--) {
        GridColumn column = this.VisibleColumns[i];
        // 忽略不可编辑Column 
        if (!column.OptionsColumn.AllowEdit) continue;
        // 当前列如果是最后一列可编辑列，则返回True，否则返回False
        return index == i;
      }
      // 如果都不可编辑，则返回False
      return false;
    }

    /// <summary>
    /// 添加新行  add by geping@2016-12-19
    /// </summary>
    private void AddNewLine() {
      var list = this.GridControl.DataSource;
      // 获取当前实例的 Type
      var t = this.DataSource.GetType();
      // 动态创建实例
      var obj = Activator.CreateInstance(t.GetGenericArguments()[0]);
      // 调用公用方法 Add
      var method = t.GetMethod("Add") ?? throw new KeyNotFoundException("对象未找到方法 Add()");
      // 将构建好的实例添加到数据源中
      method.Invoke(list, new object[] { obj });
      // 刷新数据源
      this.RefreshData();
    }

    /// <summary>  
    /// 获取GridView过滤或排序后的数据集，数据源是List类型  
    /// </summary>  
    /// <typeparam name="T">泛型对象</typeparam>  
    /// <returns></returns>  
    public IEnumerable<T> GetFilteredDataSource<T>() where T : class {
      var list = new List<T>();
      for (var i = 0; i < this.RowCount; i++) {
        if (this.IsGroupRow(i)) continue;

        var entity = this.GetRow(i) as T;
        if (entity == null) continue;

        list.Add(entity);
      }
      return list;
    }

    /// <summary>
    /// 获取GridView过滤或排序后的数据集，数据源是DataTable类型
    /// </summary>
    /// <returns></returns> 
    public DataTable GetFilteredAndSortedDataToDataTable() {
      var dt = this.GridControl.DataSource as DataTable;
      if (dt == null) { return null; }

      var result = dt.Clone();
      for (var i = 0; i < this.RowCount; i++) {
        if (this.IsGroupRow(i)) continue;

        var dr = this.GetDataRow(i);
        if (dr == null) continue;

        result.Rows.Add(dr.ItemArray);
      }
      return result;
    }

    /// <summary>
    /// 获取 GridView 过滤或排序后的数据集
    /// </summary>
    /// <see cref=">https://blog.csdn.net/u012097590/article/details/52777437"/>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public List<T> GetAllFilteredAndSortedRows<T>() where T : class {
      // DevExpress 的GridView竟然提供了相应的方法，但是奇怪的是编译器竟然没有显示，
      // 就是在GridView的基类BaseView中提供了DataController这个对象，
      // 但是这个属性后被屏蔽了，EditorBrowsable是Never状态
      return this.DataController.GetAllFilteredAndSortedRows().Cast<T>().ToList();
    }

    /// <summary>
    /// 获取选中行的数据集合，如果不存在，则返回默认构造类型
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public List<T> GetSelectedRowsList<T>() where T : class, new() {
      // 获取选中行的行号
      var rowIds = this.GetSelectedRows();

      if (rowIds == null) {
        return new List<T>();
      } else {
        return rowIds.Where(x => x >= 0).Select(x => this.GetRow(x) as T).ToList();
      }
    }

    /// <summary>
    /// 获取选中行的行号及对应行数据集合
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public List<(int rowHandle, T entity)> GetSelectedRowsCollection<T>() where T : class, new() {
      // 获取选中行的行号
      var rowIds = this.GetSelectedRows();

      if (rowIds == null) {
        return new List<(int rowHandle, T entity)>();
      } else {
        return rowIds.Where(x => x >= 0).Select(x => (x, this.GetRow(x) as T)).ToList();
      }
    }

    /// <summary>
    /// 获取GridView的选中行数据集（数据源是 DataTable） add by geping@2019-01-05
    /// 注：未测试过
    /// </summary>
    /// <returns></returns>
    public DataTable GetSelectedDataRows() {
      var dt = (this.DataSource as DataView)?.Table?.Clone();
      if (dt == null) { return new DataTable(); }

      var rowIds = this.GetSelectedRows();

      foreach (var id in rowIds) {
        var row = (this.GetRow(id) as DataRowView)?.Row;
        if (row == null) continue;

        dt.Rows.Add(row.ItemArray);
      }
      return dt;
    }

    public MyGridView CreateGridView(List<BindColumnOption> options) {
      if (options == null) { return null; }

      this.Columns.Clear();
      this.Columns.AddRange(options.Select(x => new GridColumn {
        Caption = x.Caption,
        FieldName = x.FieldName,
        Name = $"col{x.FieldName}",
        Visible = x.Visible,
        Width = x.Width,
      }).ToArray());

      foreach (var column in options.Where(x => x.FormatString != null)) {
        this.Columns[column.FieldName].DisplayFormat.FormatType = column.FormatType;
        this.Columns[column.FieldName].DisplayFormat.FormatString = column.FormatString;
      }

      return this;
    }
    #endregion

    /// <summary>
    /// 在初始化后注册一些事件
    /// </summary>
    public override void EndInit() {
      base.EndInit();

      // 针对MyGridLookUpEdit的情况
      if (string.IsNullOrWhiteSpace(this.GridControl?.Name)) return;

      // 注册鼠标左键单击事件
      this.MouseUp += new MouseEventHandler(MyGridView_MouseUp);

      // 注册鼠标右键
      if (this.EnablePopupMenuShowing) {
        this.PopupMenuShowing += DevHelper.MyGridView_PopupMenuShowing;
      }

      #region GridView行头显示行号且根据行号宽度动态自适应显示  add by geping@2020-09-24
      // 默认宽度
      this.IndicatorWidth = CalcIndicatorDefaultWidth();
      // GridView行头根据行号宽度动态自适应显示
      this.TopRowChanged += (s, e) => {
        // 自适应宽度
        this.AutoSetIndicatorWidth();
      };
      // 设置自动行号
      this.CustomDrawRowIndicator += (s, e) => {
        if (e.Info.IsRowIndicator && e.RowHandle >= 0) {
          e.Info.DisplayText = (e.RowHandle + 1).ToString();
        }
      };
      // 设置行号宽度并随行数的变化而变化
      this.RowCountChanged += (s, e) => {
        // 根据总行数设置行号宽度
        //  this.IndicatorWidth = 28 + (this.RowCount.ToString().Length - 1) * 10;                                   
        // 自适应宽度
        this.AutoSetIndicatorWidth();
      };
      #endregion

      // 在查询得到0条记录时显示自定义的字符提示/显示
      this.CustomDrawEmptyForeground += new CustomDrawEventHandler(MyGridView_CustomDrawEmptyForeground);
      // 回车跳转单元格（配合 ForwardColumnList 使用）
      this.KeyDown += new KeyEventHandler(MyGridView_KeyDown);

      // 此事件有问题，一旦启用该事件，那么回车时就会导致连续跳两格单元格的情况   modify by geping@2018-10-07
      //this.FocusedColumnChanged += this.MyGridView_FocusedColumnChanged;
      // 在gridview中光标定位到某个单元格时，如果存在子控件GridLookUpEdit，不需要展开下拉框  modify by geping@2018-10-07
      //this.ShownEditor += this.MyGridView_ShownEditor;

      #region GridView 不可编辑时的控制 add by geping@2024-04-19
      if (!this.OptionsBehavior.Editable) {
        foreach (GridColumn col in this.Columns.Where(x => x.ColumnEdit != null)) {
          // 内部的 RepositoryItemCheckEdit 也不可编辑
          if (col.ColumnEdit is RepositoryItemCheckEdit) {
            col.OptionsColumn.AllowEdit = false;
          }
        }
      }
      #endregion

      if (!string.IsNullOrWhiteSpace(this.CheckBoxSelectorField)
        && this.Columns[this.CheckBoxSelectorField] != null) {
        this.RowCellClick += (sender, e) => {
          switch (e.Column.FieldName) {
            case SmartRetailConstants.DEVEXPRESS_SELECTION_NAME:
              this.SetRowCellValue(e.RowHandle, this.Columns[this.CheckBoxSelectorField], this.IsRowSelected(e.RowHandle) ? 0 : 1);
              this.CloseEditor();
              this.RefreshRow(e.RowHandle);

              this.OnRowCellClickEventHandler(e);
              break;
          }
        };
        this.SelectionChanged += (sender, e) => {
          if (e.Action != CollectionChangeAction.Refresh) { return; }

          // 选中的区域
          var hi = this.CalcHitInfo(this.GridControl.PointToClient(Control.MousePosition));
          // 不在标题栏中点击，则返回
          if (!hi.InColumnPanel) { return; }
          // 如果不是控件自带复选框，则返回
          if (hi.Column.FieldName != SmartRetailConstants.DEVEXPRESS_SELECTION_NAME) { return; }

          for (var i = 0; i < this.RowCount; i++) {
            this.SetRowCellValue(i, this.Columns[this.CheckBoxSelectorField], this.IsRowSelected(i) ? 1 : 0);
          }

          this.CloseEditor();
          this.SyncCheckStatus();

          this.OnSelectionChangedEventHandler(e);
        };
        this.ColumnFilterChanged += (sender, e) => {
          this.SyncCheckStatus();

          this.OnColumnFilterChangedEventHandler(e);
        };
        this.BeforeLeaveRow += (sender, e) => {
          this.SyncCheckStatus();
        };
      }

      this.ReCaculateWidthByDPI();
    }

    public virtual void OnRowCellClickEventHandler(RowCellClickEventArgs e) {
      RowCellClickEventHandler?.Invoke(this, e);
    }

    public virtual void OnSelectionChangedEventHandler(SelectionChangedEventArgs e) {
      SelectionChangedEventHandler?.Invoke(this, e);
    }

    public virtual void OnColumnFilterChangedEventHandler(EventArgs e) {
      ColumnFilterChangedEventHandler?.Invoke(this, e);
    }

    #region 自带复选框同步指定字段 CheckBoxSelectorField  add by geping@2021-07-02
    /// <summary>
    /// 用指定字段状态同步刷新默认复选框状态
    /// </summary>
    public void SyncCheckStatus() {
      if (!(this.Columns[this.CheckBoxSelectorField] is GridColumn column)) { return; }

      for (var i = 0; i < this.RowCount; i++) {
        if (this.GetRowCellValue(i, column).Equals(1)) {
          this.SelectRow(i);
        } else {
          this.UnselectRow(i);
        }
      }
    }
    #endregion

    #region 计算行头宽度  add by geping@2020-09-24
    /// <summary>
    /// 自适应宽度
    /// </summary>
    private void AutoSetIndicatorWidth() {
      var width = this.CalcIndicatorBestWidth();

      if ((this.IndicatorWidth - 4 < width || this.IndicatorWidth + 4 > width) && this.IndicatorWidth != width) {
        this.IndicatorWidth = width;
      }
    }
    /// <summary>
    /// 计算默认的宽度
    /// </summary>
    /// <see cref="https://blog.csdn.net/u012097590/article/details/90898424"/>
    /// <returns></returns>
    private int CalcIndicatorDefaultWidth() {
      var grid = this.GridControl;
      var graphics = new Control().CreateGraphics();
      var rowHeight = 20; // 20是RowHeight的默认高度

      if (this.RowHeight > 0) {
        rowHeight = this.RowHeight;
      }

      var count = (grid?.Height / rowHeight) ?? 30;
      var sizeF = graphics.MeasureString(count.ToString(), this.Appearance.Row.Font);
      return Convert.ToInt32(sizeF.Width) + 20;
    }

    /// <summary>
    /// 计算行头宽度
    /// </summary>
    /// <returns></returns>
    private int CalcIndicatorBestWidth() {
      var count = this.TopRowIndex + ((GridViewInfo)this.GetViewInfo()).RowsInfo.Count;
      if (count == 0) {
        count = 30;
      }
                          
      var graphics = new Control().CreateGraphics();
      var sizeF = graphics.MeasureString(count.ToString(), this.Appearance.Row.Font);
      return Convert.ToInt32(sizeF.Width) + 20;
    }
    #endregion

    /// <summary>
    /// 
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void MyGridView_ShownEditor(object sender, EventArgs e) {
      if (((GridView)sender).ActiveEditor == null) return;

      var editor = ((GridView)sender).ActiveEditor;
      if (editor is GridLookUpEdit) {
        if (!(editor.Bounds.Contains(this.PointToClient(Cursor.Position)))) {
          var form = this.GridControl.FindForm();

          form.BeginInvoke(new MethodInvoker(() => {
            ((GridLookUpEdit)editor).ShowPopup();
          }));
        }
      }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void MyGridView_FocusedColumnChanged(object sender, FocusedColumnChangedEventArgs e) {
      this.ShowEditor();
    }

    /// <summary>
    /// 在查询得到0条记录时显示自定义的字符提示/显示
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void MyGridView_CustomDrawEmptyForeground(object sender, CustomDrawEventArgs e) {
      if (!this.EnableShowEmptyForeground) return;

      string showText = string.IsNullOrWhiteSpace(this.EmptyForegroundText) ? "没有查询到你所想要的数据!" : this.EmptyForegroundText;

      //方法一（此方法为GridView设置了数据源绑定时，可用）
      try {
        var bindingSource = this.DataSource as BindingSource;
        if (bindingSource.Count == 0) {
          var str = showText;
          var f = new Font("宋体", 10, FontStyle.Bold);
          var r = new Rectangle(e.Bounds.Top + 5, e.Bounds.Left + 5, e.Bounds.Right - 5, e.Bounds.Height - 5);
          e.Graphics.DrawString(str, f, Brushes.Black, r);
        }
      } catch {
        //方法二（此方法为GridView没有设置数据源绑定时使用，一般使用此种方法）  
        if (this.RowCount == 0) {
          var str = showText;
          var f = new Font("宋体", 10, FontStyle.Bold);
          var r = new Rectangle(e.Bounds.Left + 5, e.Bounds.Top + 5, e.Bounds.Width - 5, e.Bounds.Height - 5);
          e.Graphics.DrawString(str, f, Brushes.Black, r);
        }
      }
    }

    #region 单元格回车事件 add by geping@2016-12-19

    /// <summary>
    /// 默认情况下，回车或Tab按可编辑列跳转
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void OnDefaultKeyDown(object sender, KeyEventArgs e) {
      if (!((e.KeyCode == Keys.Enter && this.OptionsNavigation.EnterMoveNextColumn) || e.KeyCode == Keys.Tab)) return;

      GridColumn column = null;
      var index = this.VisibleColumns.IndexOf(this.FocusedColumn);

      // 光标是否在最后一列可编辑列
      if (IsLastEditableColumn(this.FocusedColumn)) {
        #region 判断是否到达末行

        if (this.FocusedRowHandle >= this.RowCount - 1) {
          // 此处可选择跳转到首行或者直接返回或者新增行
          switch (this.ForwardType) {
            case EnumForwardType.Circle:  // 首行首列
              this.MoveFirst();
              for (int i = 0; i < this.VisibleColumns.Count; i++) {
                column = this.VisibleColumns[i];
                // 忽略不可编辑Column 
                if (!column.OptionsColumn.AllowEdit) continue;
                // 当前列可编辑，则光标定位到该列
                this.FocusedColumn = column;
                return;
              }
              break;
            case EnumForwardType.Stop:    // 停止跳转，直接返回
              e.Handled = true;
              break;
            case EnumForwardType.NewRow:  // 新增行，并跳转到新行首列
              if (this.AddNewLineEventHandler == null)
                AddNewLine();
              else
                AddNewLineEventHandler();

              this.MoveNext();
              // 没有指定定位字段
              if (string.IsNullOrWhiteSpace(this.LocatedColumn)) {
                for (int i = 0; i < this.VisibleColumns.Count; i++) {
                  column = this.VisibleColumns[i];
                  // 忽略不可编辑Column 
                  if (!column.OptionsColumn.AllowEdit) continue;
                  // 当前列可编辑，则光标定位到该列
                  this.FocusedColumn = column;
                  return;
                }
              } else {
                this.FocusedColumn = this.Columns[this.LocatedColumn];
              }
              break;
          }
          return;
        }

        #endregion

        this.MoveNext();
        for (int i = 0; i < this.VisibleColumns.Count; i++) {
          column = this.VisibleColumns[i];
          // 忽略不可编辑Column 
          if (!column.OptionsColumn.AllowEdit) continue;

          // 如果是系统自带的编辑列，则直接跳过
          if (column.FieldName == SmartRetailConstants.DEVEXPRESS_SELECTION_NAME) {
            continue;
          }

          // 当前列可编辑，则光标定位到该列
          this.FocusedColumn = column;
          return;
        }
      } else {
        // 从当前定位列跳转到下一可编辑列
        for (int i = index + 1; i < this.VisibleColumns.Count; i++) {
          column = this.VisibleColumns[i];
          // 忽略不可编辑Column 
          if (!column.OptionsColumn.AllowEdit) continue;

          // 如果是系统自带的编辑列，则直接跳过
          if (column.FieldName == SmartRetailConstants.DEVEXPRESS_SELECTION_NAME) {
            continue;
          }

          // 当前列可编辑，则光标定位到该列
          this.FocusedColumn = column;
          return;
        }

        // 如果光标在不可编辑列
        for (int i = 0; i <= index; i++) {
          column = this.VisibleColumns[i];
          // 忽略不可编辑Column 
          if (!column.OptionsColumn.AllowEdit) continue;

          // 如果是系统自带的编辑列，则直接跳过
          if (column.FieldName == SmartRetailConstants.DEVEXPRESS_SELECTION_NAME) {
            continue;
          }
          // 当前列可编辑，则光标定位到该列
          this.FocusedColumn = column;
          return;
        }
      }
    }

    /// <summary>
    /// 自定义跳转，按指定列字段跳转
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void OnCustomerKeyDown(object sender, KeyEventArgs e) {
      if (!((e.KeyCode == Keys.Enter && this.OptionsNavigation.EnterMoveNextColumn) || e.KeyCode == Keys.Tab)) return;

      var fieldName = this.FocusedColumn.FieldName;

      if (this.ForwardColumnList.Contains(fieldName)) {
        var index = this.ForwardColumnList.IndexOf(fieldName);

        // 光标不在当前行指定列集合的最后一列
        if (index != this.ForwardColumnList.Count - 1) {
          this.FocusedColumn = this.Columns[this.ForwardColumnList[index + 1]];
        } else  // 光标定位当前行指定列集合的最后一列
          {
          #region 判断是否到达末行

          if (this.FocusedRowHandle >= this.RowCount - 1) {
            // 此处可选择跳转到首行或者直接返回或者新增行
            switch (this.ForwardType) {
              case EnumForwardType.Circle:  // 首行首列
                this.MoveFirst();
                this.FocusedColumn = this.Columns[this.ForwardColumnList[0]];
                break;
              case EnumForwardType.Stop:    // 停止跳转，直接返回
                e.Handled = true;
                break;
              case EnumForwardType.NewRow:  // 新增行，并跳转到新行首列
                if (this.AddNewLineEventHandler == null)
                  AddNewLine();
                else
                  AddNewLineEventHandler();

                fieldName = string.IsNullOrWhiteSpace(this.LocatedColumn) ? this.ForwardColumnList[0] : this.LocatedColumn;

                this.MoveNext();
                this.FocusedColumn = this.Columns[fieldName];
                break;
            }
            return;
          }

          #endregion

          this.MoveNext();
          this.FocusedColumn = this.Columns[this.ForwardColumnList[0]];
        }
      } else {
        this.FocusedColumn = this.Columns[this.ForwardColumnList[0]];
      }
    }

    /// <summary>
    /// 回车跳转单元格 add by geping@2016-11-25
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void MyGridView_KeyDown(object sender, KeyEventArgs e) {
      if (this.FocusedColumn.FieldName == SmartRetailConstants.DEVEXPRESS_SELECTION_NAME && e.KeyCode == Keys.Space) {
        this.SetRowCellValue(this.FocusedRowHandle, this.Columns[this.CheckBoxSelectorField], this.IsRowSelected(this.FocusedRowHandle) ? 0 : 1);

        this.CloseEditor();
        this.RefreshRow(this.FocusedRowHandle);
      }
      if (!((e.KeyCode == Keys.Enter && this.OptionsNavigation.EnterMoveNextColumn) || e.KeyCode == Keys.Tab)) { return; }

      this.CloseEditor();

      if (this.ValidateEditor() == false) {
        return;
      }

      // 未设置，则按默认规则跳转
      if (this.ForwardColumnList == null || this.ForwardColumnList.Count == 0) {
        OnDefaultKeyDown(sender, e);
      } else {
        OnCustomerKeyDown(sender, e);
      }
    }

    #endregion

    #region GridLookUpEdit 模糊匹配

    /// <summary>
    /// SetGridControlAccessMethod，主要是了MyGridLookUpEdit用
    /// </summary>
    /// <param name="newControl"></param>
    protected internal virtual void SetGridControlAccessMethod(GridControl newControl) {
      SetGridControl(newControl);
    }

    /// <summary>
    /// GridLookUpEdit的模糊匹配
    /// </summary>
    /// <param name="text"></param>
    /// <param name="displayMember"></param>
    /// <returns></returns>
    protected override string OnCreateLookupDisplayFilter(string text, string displayMember) {
      var subStringOperators = new List<CriteriaOperator>();
      foreach (var sString in text.Split(' ')) {
        var columnsOperators = new List<CriteriaOperator>();

        // TODO this.FilterColumnMode在这里无效，不知道怎么解决
        foreach (GridColumn col in this.Columns) {
          columnsOperators.Add(new FunctionOperator(FunctionOperatorType.Contains, new OperandProperty(col.FieldName), sString));
        }

        subStringOperators.Add(new GroupOperator(GroupOperatorType.Or, columnsOperators));
      }

      return new GroupOperator(GroupOperatorType.And, subStringOperators).ToString();
    }

    /// <summary>
    /// 模糊匹配的字符串
    /// </summary>
    protected virtual internal string GetExtraFilterText => base.ExtraFilterText;

    //protected override DevExpress.Data.Filtering.CriteriaOperator CreateAutoFilterCriterion(DevExpress.XtraGrid.Columns.GridColumn column, DevExpress.XtraGrid.Columns.AutoFilterCondition condition, object _value, string strVal) {
    //  if (column.ColumnType == typeof(DateTime) && strVal.Length > 0) {
    //    BinaryOperatorType type = BinaryOperatorType.Equal;
    //    string operand = string.Empty;
    //    if (strVal.Length > 1) {
    //      operand = strVal.Substring(0, 2);
    //      if (operand.Equals(">="))
    //        type = BinaryOperatorType.GreaterOrEqual;
    //      else if (operand.Equals("<="))
    //        type = BinaryOperatorType.LessOrEqual;
    //    }
    //    if (type == BinaryOperatorType.Equal) {
    //      operand = strVal.Substring(0, 1);
    //      if (operand.Equals(">"))
    //        type = BinaryOperatorType.Greater;
    //      else if (operand.Equals("<"))
    //        type = BinaryOperatorType.Less;
    //    }
    //    if (type != BinaryOperatorType.Equal) {

    //      string val = strVal.Replace(operand, string.Empty);
    //      try {
    //        DateTime dt = DateTime.ParseExact(val, "d", column.RealColumnEdit.EditFormat.Format);
    //        return new BinaryOperator(column.FieldName, dt, type);
    //      } catch {
    //        return null;
    //      }
    //    }
    //  }
    //  return base.CreateAutoFilterCriterion(column, condition, _value, strVal);
    //}

    #endregion

    /// <summary>
    /// 单击进入编辑状态
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    public virtual void MyGridView_MouseUp(object sender, MouseEventArgs e) {
      // 非左键
      if (e.Button != MouseButtons.Left) return;

      GridView view = null;

      // 传递对象
      if (sender is BandedGridView) {
        view = sender as BandedGridView;
      } else if (sender is GridView) {
        view = sender as GridView;
      }

      // 选择多列
      if (view.GetSelectedCells().Length > 1) return;

      // 选中的区域
      GridHitInfo hi = view.CalcHitInfo(e.Location);

      // 不在单元格中
      if (hi.InRowCell == false) return;

      // 不能编辑
      if (hi.Column.OptionsColumn.AllowEdit == false) return;
      // 控件自带复选框不处理，否则会造成无效
      if (hi.Column.FieldName == SmartRetailConstants.DEVEXPRESS_SELECTION_NAME) return;

      try {
        // 根据不同列分别处理
        if (hi.Column.RealColumnEdit is RepositoryItemCheckEdit) {
          view.FocusedColumn = hi.Column;
          view.FocusedRowHandle = hi.RowHandle;

          var columnEdit = (RepositoryItemCheckEdit)hi.Column.RealColumnEdit;
          object value2 = null;
          var value = view.GetRowCellValue(hi.RowHandle, hi.Column);
          if (value.To<int>() == columnEdit.ValueChecked.To<int>()) {
            value2 = columnEdit.ValueUnchecked;
          } else {
            value2 = columnEdit.ValueChecked;
          }

          view.SetFocusedRowCellValue(view.FocusedColumn, value2);
        } else if ((hi.Column.RealColumnEdit as RepositoryItemPopupBase) != null) {
          view.FocusedColumn = hi.Column;
          view.FocusedRowHandle = hi.RowHandle;
          this.GridControl.BeginInvoke(new MethodInvoker(delegate ()
          {
            if (view.ActiveEditor is PopupBaseEdit) {
              view.ShowEditor();
              //(view.ActiveEditor as PopupBaseEdit).ShowPopup();
            }
          }));
        }
      } catch { } finally {
        // add by geping@2017-02-16，加上此句，默认单击单元格全选
        this.ShowEditor();
      }
    }

    #region 重写属性

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    protected override ColumnViewOptionsView CreateOptionsView() {
      return new CustomGridOptionsView();
      //return base.CreateOptionsView();
    }

    ///// <summary>
    ///// 
    ///// </summary>
    ///// <returns></returns>
    //protected override ColumnViewOptionsFilter CreateOptionsFilter() {
    //  return new CustomGridOptionsFilter();
    //  //return base.CreateOptionsFilter();
    //}

    ///// <summary>
    ///// 
    ///// </summary>
    ///// <returns></returns>
    //protected override ColumnViewOptionsSelection CreateOptionsSelection() {
    //  var dd = base.CreateOptionsSelection();

    //  return new CustomGridOptionsSelection();
    //}

    ///// <summary>
    ///// 
    ///// </summary>
    ///// <returns></returns>
    //protected override GridOptionsDetail CreateOptionsDetail() {
    //  return new CustomGridOptionsDetail();
    //}

    ///// <summary>
    ///// 
    ///// </summary>
    ///// <returns></returns>
    //protected override ColumnViewOptionsBehavior CreateOptionsBehavior() {
    //  return new CustomGridOptionsBehavior();
    //}

    ///// <summary>
    ///// 
    ///// </summary>
    ///// <returns></returns>
    //protected override ViewPrintOptionsBase CreateOptionsPrint() {
    //  return new CustomGridOptionsPrint();
    //}

    ///// <summary>
    ///// 
    ///// </summary>
    ///// <returns></returns>
    //protected override GridOptionsNavigation CreateOptionsNavigation() {
    //  return new CustomGridOptionsNavigation();
    //}

    ///// <summary>
    ///// 
    ///// </summary>
    ///// <returns></returns>
    //protected override GridOptionsCustomization CreateOptionsCustomization() {
    //  return new CustomGridOptionsCustomization();
    //}

    #endregion
  }
  #endregion

  #region MyGridHandler processes the mouse and keyboard.

  /// <summary>
  /// processes the mouse and keyboard.
  /// </summary>
  public class MyGridHandler : DevExpress.XtraGrid.Views.Grid.Handler.GridHandler {
    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="gridView"></param>
    public MyGridHandler(GridView gridView)
        : base(gridView) {
    }

    /// <summary>
    /// 重写事件 OnKeyDown
    /// </summary>
    /// <param name="e"></param>
    protected override void OnKeyDown(KeyEventArgs e) {
      try {
        base.OnKeyDown(e);
      } catch { }

      // Ctrl + C 复制单元格
      if (e.Control & e.KeyCode == Keys.C) {
        try {
          Clipboard.SetDataObject(this.View.GetFocusedRowCellDisplayText(this.View.FocusedColumn));
          e.Handled = true;
        } catch { }
      }

      // 模拟删除事件
      //if (e.KeyData == Keys.Delete && View.State == GridState.Normal) {
      //  View.DeleteRow(View.FocusedRowHandle);
      //}
    }
  }

  #endregion

  #region MyGridViewInfoRegistrator

  /// <summary>
  /// GridInfoRegistrator，注册MyGridView
  /// </summary>
  public class MyGridViewInfoRegistrator : GridInfoRegistrator {
    protected const string MyGridViewName = "MyGridView";

    /// <summary>
    /// Default View Name
    /// </summary>
    public override string ViewName => MyGridViewName;

    /// <summary>
    /// 创建GridView
    /// </summary>
    /// <param name="grid"></param>
    /// <returns></returns>
    public override BaseView CreateView(GridControl grid) {
      // 主要为了GridLookUpEdit用
      var view = new MyGridView();
      view.SetGridControlAccessMethod(grid);
      view.Initialize();
      return view;
    }

    /// <summary>
    /// CreateViewInfo
    /// </summary>
    /// <param name="view"></param>
    /// <returns></returns>
    public override BaseViewInfo CreateViewInfo(BaseView view) {
      return new MyGridViewInfo(view as MyGridView);
    }

    /// <summary>
    /// 创建自定义的事件
    /// </summary>
    /// <param name="view"></param>
    /// <returns></returns>
    public override BaseViewHandler CreateHandler(BaseView view) {
      return new MyGridHandler(view as MyGridView);
    }

    /// <summary>
    /// 创建自定义的绘画
    /// </summary>
    /// <param name="view"></param>
    /// <returns></returns>
    public override BaseViewPainter CreatePainter(BaseView view) {
      return new MyGridPainter(view as GridView);
    }
  }

  #endregion

  #region MyGridViewInfo calculates view information for drawing a grid view on screen.

  /// <summary>
  /// calculates view information for drawing a grid view on screen.
  /// </summary>
  public class MyGridViewInfo : GridViewInfo {
    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="gridView"></param>
    public MyGridViewInfo(GridView gridView)
        : base(gridView) { }
  }

  #endregion

  #region GridPainter

  /// <summary>
  /// 自定义画笔
  /// </summary>
  public class MyGridPainter : GridPainter {
    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="view"></param>
    public MyGridPainter(GridView view) : base(view) { }

    /// <summary>
    /// 创建视图
    /// </summary>
    public virtual new MyGridView View => (MyGridView)base.View;

    /// <summary>
    /// 绘制行信息,主要为了MyGridLookUpEdit
    /// </summary>
    /// <param name="e"></param>
    /// <param name="cell"></param>
    protected override void DrawRowCell(GridViewDrawArgs e, GridCellInfo cell) {
      cell.ViewInfo.MatchedStringUseContains = true;
      // 匹配表达式
      cell.ViewInfo.MatchedString = this.View.GetExtraFilterText;
      // 单元格状态
      cell.State = GridRowCellState.Dirty;
      // 更新行
      e.ViewInfo.UpdateCellAppearance(cell);
      // Draw
      base.DrawRowCell(e, cell);
    }
  }

  #endregion
}
